"""
双均线策略是量化交易中经典的技术分析策略之一，其核心原理基于两条不同周期移动平均线的交叉关系，判断股票的买卖时机。
该策略通过计算短期均线（如 5 日均线）和长期均线（如 20 日均线），对比二者的相对位置变化，形成交易信号。
当短期均线从下向上穿越长期均线，即发生 “金叉” 时，表明短期股价上涨趋势增强，市场多头力量占据主导，此时发出 “强烈买入信号”；
反之，当短期均线从上向下穿越长期均线，即出现 “死叉”，意味着短期股价下跌趋势显著，空头力量占据优势，策略提示 “强烈卖出信号”。
若短期均线持续位于长期均线上方，则维持 “持有” 状态，表明市场处于多头趋势；若短期均线持续位于长期均线下方，则建议 “观望”，提示市场处于空头趋势。
当两条均线走势趋于平行、间距缩小时，策略判定为 “中性”，此时市场方向不明，建议谨慎操作。
该策略逻辑简洁明了，通过对股价趋势的平滑处理，过滤短期波动，捕捉中长期趋势机会，适用于趋势性较强的市场环境。
在实际应用中，通过对不同周期均线的组合优化，可适配各类股票的特性，为投资者提供客观、量化的交易参考。
"""

import pandas as pd
from sqlalchemy import create_engine
import matplotlib.pyplot as plt
from datetime import datetime

# 创建数据库连接引擎
engine = create_engine('mysql+pymysql://root:@127.0.0.1:3306/stocks?charset=utf8mb4&use_unicode=1')

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False


def generate_ma_signals():
    """
    为所有股票生成双均线策略信号并以表格图表展示（优化版）
    """
    # 获取所有股票代码和名称
    query = "SELECT ts_code, name FROM stocks_info"
    stocks = pd.read_sql(query, con=engine)

    data = []
    for _, stock in stocks.iterrows():
        ts_code = stock['ts_code']
        name = stock['name']

        try:
            # 获取最近21个交易日数据(足够计算20日均线)
            query = f"""
                SELECT trade_date, close 
                FROM stocks_daily 
                WHERE ts_code = '{ts_code}' 
                ORDER BY trade_date DESC 
                LIMIT 21
            """
            df = pd.read_sql(query, con=engine)

            if len(df) < 21:
                signal = "数据不足，无法计算均线"
            else:
                # 按日期升序排列计算
                df = df.sort_values('trade_date')

                # 计算均线
                df['5_ma'] = df['close'].rolling(window=5).mean()
                df['20_ma'] = df['close'].rolling(window=20).mean()

                # 最新数据
                latest = df.iloc[-1]
                prev = df.iloc[-2]

                # 生成信号
                if latest['5_ma'] > latest['20_ma'] and prev['5_ma'] <= prev['20_ma']:
                    signal = "强烈买入信号(金叉)"
                elif latest['5_ma'] < latest['20_ma'] and prev['5_ma'] >= prev['20_ma']:
                    signal = "强烈卖出信号(死叉)"
                elif latest['5_ma'] > latest['20_ma']:
                    signal = "持有(多头趋势)"
                elif latest['5_ma'] < latest['20_ma']:
                    signal = "观望(空头趋势)"
                else:
                    signal = "中性(均线粘合)"

            data.append([name, ts_code, signal])

        except Exception as e:
            data.append([name, ts_code, f"处理错误 - {str(e)}"])

    df_result = pd.DataFrame(data, columns=['股票名称', '股票代码', '信号'])

    # 创建表格图表
    fig, ax = plt.subplots(figsize=(10, 6))
    ax.axis('off')

    # 创建表格
    table = ax.table(
        cellText=df_result.values,
        colLabels=df_result.columns,
        loc='center',
        cellLoc='center'
    )

    # 设置表格样式
    table.auto_set_font_size(False)
    table.set_fontsize(10)
    # 调整水平缩放因子来调宽表格
    table.scale(1.5, 1.5)

    # 设置列宽
    table.auto_set_column_width([0, 1, 2])

    # 手动调整列宽示例
    for col in range(3):
        if col == 0:
            width = 2
        elif col == 1:
            width = 3
        else:
            width = 4
        for row in range(len(df_result) + 1):
            table._cells[(row, col)].set_width(width)

    # 设置表格内容居中
    for key, cell in table.get_celld().items():
        cell.set_text_props(ha='center', va='center')
        cell.set_edgecolor('lightgray')  # 添加细边框

    # 设置表头样式
    for i in range(len(df_result.columns)):
        cell = table[(0, i)]
        cell.set_facecolor('#40466e')
        cell.get_text().set_color('white')
        cell.set_fontsize(12)

    # 遍历表格单元格，根据信号设置背景颜色
    for i in range(1, len(df_result) + 1):
        signal = df_result.iloc[i - 1]['信号']
        if '买入' in signal:
            color = '#ff6b6b'  # 红色
            table[(i, 0)].set_facecolor(color)
            table[(i, 1)].set_facecolor(color)
            table[(i, 2)].set_facecolor(color)
            for j in range(3):
                table[(i, j)].get_text().set_color('white')
        elif '卖出' in signal:
            color = '#51cf66'  # 绿色
            table[(i, 0)].set_facecolor(color)
            table[(i, 1)].set_facecolor(color)
            table[(i, 2)].set_facecolor(color)
            for j in range(3):
                table[(i, j)].get_text().set_color('white')
        elif '观望' in signal:
            color = '#339af0'  # 蓝色
            table[(i, 0)].set_facecolor(color)
            table[(i, 1)].set_facecolor(color)
            table[(i, 2)].set_facecolor(color)
            for j in range(3):
                table[(i, j)].get_text().set_color('white')

    # 计算表格占据的垂直空间比例
    table_rows = len(df_result) + 1  # 包括表头行
    fig_height = fig.get_size_inches()[1]  # 获取图形高度(英寸)
    row_height = 0.4  # 每行的近似高度(英寸)

    # 计算表格占据的总高度(英寸转换为相对高度)
    table_total_height = (table_rows * row_height) / (fig_height * 72)  # 72 points per inch

    # 动态调整时间戳位置
    if table_total_height > 0.85:
        timestamp_y = 0.01  # 表格很高时放在最底部
    elif table_total_height > 0.6:
        timestamp_y = 0.02  # 表格较高时
    else:
        timestamp_y = 0.03  # 表格较小时

    # 添加时间戳
    current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    time_text = ax.text(
        0.98,  # x位置(0.98表示靠近右侧)
        timestamp_y,  # y位置(动态调整)
        f"生成时间: {current_time}",
        transform=ax.transAxes,  # 使用相对坐标
        ha='right',  # 右对齐
        va='bottom',  # 底部对齐
        fontsize=10,
        bbox=dict(
            facecolor='white',
            alpha=0.8,
            edgecolor='gray',
            boxstyle='round,pad=0.3'
        )
    )
    # 添加标题
    plt.title(
        '股票双均线策略信号汇总',
        pad=20,  # 增加标题与表格的间距
        fontsize=14,
        fontweight='bold'
    )
    # 调整布局
    plt.tight_layout()
    plt.subplots_adjust(
        top=0.9,  # 为标题留出空间
        bottom=0.1  # 为时间戳留出空间
    )

    plt.show()

# 生成并展示信号图表
generate_ma_signals()
